﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
using System.Windows;
using System.Globalization;
using System.Windows.Data;
using System.Xaml;
using System.Reflection;
using System.Windows.Media;
using System.Windows.Threading;

using Invini.Tools;

namespace Invini.Localization
{
    public class LocalizeExtension : MarkupExtension
    {
        private class Localization
        {
            private ILocalizationManager localizationManager;
            private object target;
            private object targetProperty;
            private object root;

            private LocalizationSet set;
            private object defaultContent;
            private IValueConverter converter;

            private object content;

            public Localization(ILocalizationManager localizationManager,
                object target,
                object targetProperty,
                object root,
                LocalizationSet set,
                object defaultContent,
                IValueConverter converter)
            {
                this.localizationManager = localizationManager;
                this.target = target;
                this.targetProperty = targetProperty;
                this.root = root;
                this.set = set;
                this.defaultContent = defaultContent;
                this.converter = converter;

                bool subscribeToEvents = false;
                if(root is FrameworkElement)
                {
                    (root as FrameworkElement).Unloaded += this.unloaded;
                    subscribeToEvents = true;
                }
                else if(root is IUnloadable)
                {
                    (root as IUnloadable).Unloaded += this.unloaded;
                    subscribeToEvents = true;
                }

                if (subscribeToEvents)
                {
                    this.localizationManager.CultureChanged += this.cultureChanged;
                    this.localizationManager.ContextChanged += this.contextChanged;
                }
            }
            public object GetContent(CultureInfo targetCulture)
            {
                this.content = this.findContent(targetCulture);

                if (this.converter != null)
                {
                    Type propertyType = null;
                    if (this.targetProperty is DependencyProperty)
                        propertyType = (this.targetProperty as DependencyProperty).PropertyType;
                    if (this.targetProperty is PropertyInfo)
                        propertyType = (this.targetProperty as PropertyInfo).PropertyType;

                    this.content = this.converter.Convert(this.content, propertyType, null, targetCulture);
                }

                if (this.target == null || this.targetProperty == null)
                    return this.content;

                if (this.targetProperty is DependencyProperty)
                {
                    if (this.target is DependencyObject)
                    {
                        DependencyObject dependencyTarget = this.target as DependencyObject;
                        if (target is DispatcherObject)
                        {
                            DispatcherObject dispatcherTarget = this.target as DispatcherObject;
                            if (dispatcherTarget.CheckAccess())
                                dependencyTarget.SetValue((targetProperty as DependencyProperty), this.content);
                            else
                                dispatcherTarget.Dispatcher.Invoke(delegate { dependencyTarget.SetValue((targetProperty as DependencyProperty), this.content); });
                        }
                        else
                            dependencyTarget.SetValue((targetProperty as DependencyProperty), this.content);
                    }

                    return this.content;
                }
                if (this.targetProperty is PropertyInfo)
                {
                    PropertyInfo propertyInfo = this.targetProperty as PropertyInfo;
                    if(propertyInfo.CanWrite)
                        propertyInfo.SetValue(this.target, this.content);
                }

                return this.content;
            }

            private void contextChanged(object sender, LocalizationContextChangedEventArgs e)
            {
                if (e.Target != this.target)
                    return;

                if (this.content != null && this.content is FrameworkElement)
                    (this.content as FrameworkElement).DataContext = e.NewContext;
            }

            private void cultureChanged(object sender, LocalizationCultureChangedEventArgs e)
            {
                this.GetContent(e.NewCulture);
            }

            private void unloaded()
            {
                if (root is FrameworkElement)
                    (root as FrameworkElement).Unloaded -= this.unloaded;
                else if (root is IUnloadable)
                    (root as IUnloadable).Unloaded -= this.unloaded;

                this.localizationManager.CultureChanged -= this.cultureChanged;
                this.localizationManager.ContextChanged -= this.contextChanged;
            }
            private void unloaded(IUnloadable sender)
            {
                this.unloaded();
            }
            private void unloaded(object sender, RoutedEventArgs e)
            {
                this.unloaded();
            }

            private object findContent(CultureInfo targetCulture)
            {
                return this.localizationManager.GetLocalizedContent(this.set, this.defaultContent, null, this.target as DependencyObject).Content;
            }
            
        }

        [ConstructorArgument("set")]
        public LocalizationSet Set
        {
            get;
            set;
        }
        public object DefaultContent
        {
            get;
            set;
        }

        public IValueConverter Converter
        {
            get;
            set;
        }

        public LocalizeExtension()
        {
        }
        public LocalizeExtension(LocalizationSet set)
        {

            this.Set = set;
        }
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            object root = null;
            object target = null;
            object targetProperty = null;

            if (serviceProvider != null)
            {
                IProvideValueTarget targetProvider = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
                if (targetProvider != null)
                {
                    target = targetProvider.TargetObject;
                    if (target.GetType().FullName == "System.Windows.SharedDp" || target is Setter)
                        return this;

                    targetProperty = targetProvider.TargetProperty;
                }

                IRootObjectProvider rootProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
                if (rootProvider != null)
                    root = rootProvider.RootObject;

                if (root == null)
                    root = target;
            }

            Localization localization = new Localization(LocalizationManager.Current,
                target, targetProperty, root, this.Set, this.DefaultContent, this.Converter);

            return localization.GetContent(LocalizationManager.Current.Settings.Culture);
        }
    }
}
